home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The Atari Compendium
/
The Atari Compendium (Toad Computers) (1994).iso
/
files
/
umich
/
network
/
ka9q
/
nhclb120.zoo
/
nr4.c
< prev
next >
Wrap
C/C++ Source or Header
|
1992-02-11
|
19KB
|
742 lines
/* net/rom level 4 (transport) protocol implementation
* Copyright 1989 by Daniel M. Frank, W9NK. Permission granted for
* non-commercial distribution only.
*/
#include <stdio.h>
#include "global.h"
#include "mbuf.h"
#include "timer.h"
#include "ax25.h"
#include "lapb.h"
#include "netrom.h"
#include "nr4.h"
#include <ctype.h>
#undef NR4DEBUG
/* Globals: */
/* The circuit table */
struct nr4circp Nr4circuits[NR4MAXCIRC] ;
/* Various limits */
unsigned Nr4window = 4 ; /* Max window to negotiate */
unsigned Nr4retries = 10 ; /* Max retries */
unsigned Nr4qlimit = 2048 ; /* Max bytes on receive queue */
/* Timers */
long Nr4irtt = 15000 ; /* Initial round trip time */
long Nr4acktime = 3000 ; /* ACK delay timer */
long Nr4choketime = 180000 ; /* CHOKEd state timeout */
/* This function is called when a net/rom layer four frame */
/* is discovered inside a datagram addressed to us */
void
nr4input(hdr,bp)
struct nr4hdr *hdr ;
struct mbuf *bp ;
{
struct nr4hdr rhdr ;
struct nr4cb *cb ;
struct ax25_addr dest ;
int op ;
unsigned window ;
int acceptc ; /* indicates that connection should be accepted */
int newconn ; /* indicates that this is a new incoming */
/* connection. You'll see. */
int gotchoke ; /* The choke flag was set in this packet */
op = hdr->opcode & NR4OPCODE ; /* Mask off flags */
if (op == NR4OPCONRQ) { /* process connect request first */
acceptc = 1 ;
newconn = 0 ;
/* These fields are sent regardless of success */
rhdr.yourindex = hdr->u.conreq.myindex ;
rhdr.yourid = hdr->u.conreq.myid ;
dest = hdr->u.conreq.node ;
/* Check to see if we have already received a connect */
/* request for this circuit. */
if ((cb = match_n4circ((int)hdr->u.conreq.myindex, (int)hdr->u.conreq.myid,
&hdr->u.conreq.user, &hdr->u.conreq.node))
== NULLNR4CB) { /* No existing circuit if NULL */
/* Try to get a new circuit */
if ((cb = new_n4circ()) == NULLNR4CB) {
acceptc = 0 ;
} else {
/* Window is set to min of the offered and local windows */
window = hdr->u.conreq.window > Nr4window ?
Nr4window : hdr->u.conreq.window ;
if (init_nr4window(cb, window) == -1) {
free_n4circ(cb) ;
acceptc = 0 ;
} else {
/* Set up control block */
cb->yournum = hdr->u.conreq.myindex ;
cb->yourid = hdr->u.conreq.myid ;
cb->user = hdr->u.conreq.user ;
cb->node = hdr->u.conreq.node ;
cb->luser = mycall ;/* we are local user on incomings */
cb->srtt = Nr4irtt ;/* Default round trip time */
nr4defaults(cb) ; /* set up timers, window pointers */
cb->s_upcall = nr4_incom ;
cb->state = NR4STDISC ;
newconn = 1 ;
} /* End if window successfully allocated */
} /* End if new circuit available */
} /* End if no existing circuit matching parameters */
/* Now set up response */
if (!acceptc) {
rhdr.opcode = NR4OPCONAK | NR4CHOKE ;/* choke means reject */
rhdr.u.conack.myindex = 0 ;
rhdr.u.conack.myid = 0 ;
rhdr.u.conack.window = 0 ;
} else {
rhdr.opcode = NR4OPCONAK ;
rhdr.u.conack.myindex = cb->mynum ;
rhdr.u.conack.myid = cb->myid ;
rhdr.u.conack.window = cb->window ;
}
nr4sframe(&dest, &rhdr, NULLBUF) ;
/* Why, you ask, do we wait until now for the state change */
/* upcall? Well, it's like this: if the state change triggers */
/* something like the mailbox to send its banner, the banner */
/* would have gone out *before* the conn ack if we'd done this */
/* in the code above. This is what happens when you don't plan */
/* too well. Learn from my mistakes :-) */
if (newconn)
nr4state(cb, NR4STCON) ;/* connected (no 3-way handshake) */
free_p(bp) ;
return ;
} /* end connect request code */
/* validate circuit number */
if ((cb = get_n4circ((int)hdr->yourindex, (int)hdr->yourid)) == NULLNR4CB) {
free_p(bp) ;
return ;
}
/* Check for choke flag */
if (hdr->opcode & NR4CHOKE)
gotchoke = 1 ;
else
gotchoke = 0 ;
/* Here's where the interesting stuff gets done */
switch (cb->state) {
case NR4STCPEND:
switch (op) {
case NR4OPCONAK:
stop_timer(&cb->tcd) ;
if (gotchoke) { /* connect rejected */
cb->dreason = NR4RREFUSED ;
nr4state(cb, NR4STDISC) ;
break ;
}
cb->yournum = hdr->u.conack.myindex ;
cb->yourid = hdr->u.conack.myid ;
window = hdr->u.conack.window > Nr4window ?
Nr4window : hdr->u.conack.window ;
if (init_nr4window(cb, window) == -1) {
cb->dreason = NR4RRESET ;
nr4state(cb, NR4STDISC) ;
} else {
nr4defaults(cb) ; /* set up timers, window pointers */
if (cb->cdtries == 1) /* No retries */
cb->srtt = cb->tcd.count * MSPTICK ;/* Use measured rtt */
else
cb->srtt = Nr4irtt ; /* else use default */
nr4state(cb, NR4STCON) ;
nr4output(cb) ; /* start sending anything on the txq */
}
break ;
default: /* We can't respond to anything else without */
/* Their ID and index */
free_p(bp) ;
return ;
}
break ;
case NR4STCON:
switch (op) {
case NR4OPDISRQ:
/* format reply packet */
rhdr.opcode = NR4OPDISAK ;
rhdr.yourindex = cb->yournum ;
rhdr.yourid = cb->yourid ;
nr4sframe(&cb->node, &rhdr, NULLBUF) ;
cb->dreason = NR4RREMOTE ;
nr4state(cb, NR4STDISC) ;
break ;
case NR4OPINFO:
/* Do receive frame processing */
nr4rframe(cb, hdr->u.info.txseq, bp) ;
/* Reset the choke flag if no longer choked. Processing */
/* the ACK will kick things off again. */
if (cb->choked && !gotchoke) {
stop_timer(&cb->tchoke) ;
cb->choked = 0 ;
}
/* We delay processing the receive sequence number until */
/* now, because the ACK might pull more off the txq and send */
/* it, and we want the implied ACK in those frames to be right */
/* Only process NAKs if the choke flag is off. It appears */
/* that NAKs should never be sent with choke on, by the way, */
/* but you never know, considering that there is no official */
/* standard for this protocol */
if (hdr->opcode & NR4NAK && !gotchoke)
nr4gotnak(cb, hdr->u.info.rxseq) ;
/* We always do ACK processing, too, since the NAK of one */
/* packet may be the implied ACK of another. The gotchoke */
/* flag is used to prevent sending any new frames, since */
/* we are just going to purge them next anyway if this is */
/* the first time we've seen the choke flag. If we are */
/* already choked, this call will return immediately. */
nr4ackours(cb, hdr->u.info.rxseq, gotchoke) ;
/* If we haven't seen the choke flag before, purge the */
/* send window and set the timer and the flag. */
if (!cb->choked && gotchoke)
nr4choke(cb) ;
break ;
case NR4OPACK:
if (cb->choked && !gotchoke) { /* clear choke if appropriate */
stop_timer(&cb->tchoke) ;
cb->choked = 0 ;
}
if (hdr->opcode & NR4NAK && !gotchoke)
nr4gotnak(cb, hdr->u.ack.rxseq) ; /* process NAKs */
nr4ackours(cb, hdr->u.ack.rxseq, gotchoke) ; /* and ACKs */
if (!cb->choked && gotchoke) /* First choke seen */
nr4choke(cb) ; /* Set choke status */
break ;
}
break ;
case NR4STDPEND:
switch (op) {
case NR4OPDISAK:
cb->dreason = NR4RNORMAL ;
nr4state(cb, NR4STDISC) ;
break ;
case NR4OPINFO:
/* We can still do receive frame processing until */
/* the disconnect acknowledge arrives, but we won't */
/* bother to process ACKs, since we've flushed our */
/* transmit buffers and queue already. */
nr4rframe(cb, hdr->u.info.txseq, bp) ;
break ;
}
} /* End switch(state) */
}
/* Send a net/rom layer 4 frame. bp should be NULLBUF unless the frame
* type is info.
*/
void
nr4sframe(dest, hdr, bp)
struct ax25_addr *dest ;
struct nr4hdr *hdr ;
struct mbuf *bp ;
{
void nr3output() ;
struct mbuf *n4b ;
if ((n4b = htonnr4(hdr)) == NULLBUF) {
free_p(bp) ;
return ;
} else {
append(&n4b, bp) ;
nr3output(dest, n4b) ;
}
}
/* Receive frame processing */
void
nr4rframe(cb, rxseq, bp)
stru